package lexer

import (
	"monkey/token"
)

type Lexer struct{
	input string
	position int//所输入字符串中的当前位置(指向当前字符)
	readPosition int//所输入字符串中的当前读取位置
	//(指向当前字符之后的一个字符)
	ch byte//当前正在查看的字符
}
//readPosition始终指向所输入字符串中的“下一个”字符
//，position则指向所输入字符串中与ch字节对应的字符。

func New(input string) *Lexer{
	l := &Lexer{input: input}
	l.readChar()
	return l
}

//readChar 读取input中的下一个字符，并前移其在input中的位置
func (l *Lexer)	readChar(){
	if l.readPosition >= len(l.input){//是否到达input的末尾
		l.ch = 0//NULL字符的ASCII编码，表示文件结尾
	}else{
		l.ch = l.input[l.readPosition]
	}
	l.position = l.readPosition
	l.readPosition++
}

func (l *Lexer) NextToken() token.Token{
	var tok token.Token
	l.skipWhitespace()
	switch l.ch{
	case '=':
		if l.peekChar() == '='{
			ch := l.ch
			l.readChar()
			literal := string(ch)+string(l.ch)
			tok = token.Token{Type:token.EQ,Literal:literal}
		}else{
			tok = token.NewToken(token.ASSIGN,l.ch)
		}
	case '!':
		if l.peekChar() == '='{
			ch := l.ch
			l.readChar()
			literal := string(ch)+string(l.ch)
			tok = token.Token{Type:token.NOT_EQ,Literal:literal}
		}else{
			tok = token.NewToken(token.BANG,l.ch)
		}
	case '+':
		tok = token.NewToken(token.PLUS,l.ch)
	case '-':
		tok = token.NewToken(token.MINUS,l.ch)
	case '/':
		tok = token.NewToken(token.SLASH,l.ch)
	case '*':
		tok = token.NewToken(token.ASTERISK,l.ch)
	case '<':
		tok = token.NewToken(token.LT,l.ch)
	case '>':
		tok = token.NewToken(token.GT,l.ch)
	case ';':
		tok = token.NewToken(token.SEMICOLON,l.ch)
	case '(':
		tok = token.NewToken(token.LPAREN,l.ch)
	case ')':
		tok = token.NewToken(token.RPAREN,l.ch)
	case ',':
		tok = token.NewToken(token.COMMA,l.ch)
	case '{':
		tok = token.NewToken(token.LBRACE,l.ch)
	case '}':
		tok = token.NewToken(token.RBRACE,l.ch)
	case 0:
		tok.Literal = ""
		tok.Type =  token.EOF
	case '"':
		tok.Type = token.STRING
		tok.Literal = l.readString()
	case '[':
		tok = token.NewToken(token.LBRACKET,l.ch)
	case ']':
		tok = token.NewToken(token.RBRACKET,l.ch)
	case ':':
		tok = token.NewToken(token.COLON,l.ch)
	default:
		if isLetter(l.ch){
			tok.Literal = l.readIdentifier()
			tok.Type = token.LookupIdent(tok.Literal)
			return tok
		} else if isDigit(l.ch){
			tok.Type = token.INT
			tok.Literal = l.readNumber()
			return tok
		} else{
			tok = token.NewToken(token.ILLEGAL,l.ch)
		}
	}
	l.readChar()
	return tok
}
//readIdentifier 识别标识符
func (l *Lexer) readIdentifier() string{
	position := l.position
	for isLetter(l.ch){
		l.readChar()
	}
	return l.input[position:l.position]
}
//readNumber 识别数字
func (l *Lexer) readNumber()string{
	position := l.position
	for isDigit(l.ch){
		l.readChar()
	}
	return l.input[position:l.position]
}
//skipWhitespace 跳过空白符号
func (l *Lexer) skipWhitespace(){
	for l.ch == ' ' || l.ch == '\t' || l.ch == '\n' || l.ch == '\r'{
		l.readChar()
	}
}
//peekChar peekChar()与readChar()非常类似，
//但这个函数不会前移l.position和l.readPosition。
//它的目的只是窥视一下输入中的下一个字符，不会移动位于输入中的指针位置，
//这样就能知道下一步在调用readChar()时会返回什么。
//大多数词法分析器和语法分析器具有这样的“窥视”函数，且大部分情况是用来向前看一个字符的。
func (l *Lexer) peekChar() byte{
	if l.readPosition >= len(l.input){
		return 0
	}else{
		return l.input[l.readPosition]
	}
}

func isLetter(ch byte) bool{
	return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_'
}

func isDigit(ch byte) bool{
	return '0' <= ch && ch <= '9'
}

func (l *Lexer) readString() string{
	position := l.position + 1
	for{
		l.readChar()
		//遇到闭双引号或到达输入末尾（end of the input）则读取完毕
		if l.ch == '"' || l.ch == 0{
			break
		}
	}
	return l.input[position:l.position]
}